import acroMLUtils from '../acroML.Utils.mjs';

let g_lastClassID=0;
let g_classMethods={};
let g_classNameGetters=[];
let g_rendorHandlers=[];

function checkClassID(c){
  let isExists=true;
  let classID=c._acroml_classID;
  if (classID==undefined){
    isExists=false;
    classID=g_lastClassID;
    g_lastClassID++;
    c._acroml_classID=classID;
  }
  return {classID,isExists};
}

function getClassMethod(c){
  let classID=c._acroml_classID;
  let m=null;
  if (classID!=undefined) m=g_classMethods[classID];
  return {classID,method:m};
}

let acroMLClassMethod={
  /**
   * 为vue组件类别注册一个翻译函数。
   * 如果用组件类比名称，众多的第3方组件，遇到名称重复怎么办？只能用类别。
   * @param {string|object} c vue组件类别或类别名称
   * @param {function} m 翻译函数，function(t,instWrap)
   */
  register(c,m){
    let key;
    if (typeof c=='string') key=c;
    else{
      if (c.name && (c.name!='Object')) key=c.name;
      else if (acroMLUtils.isObject(c)){
        let {classID,isExists}=checkClassID(c);
        key=classID;
      }
    }
    if (g_classMethods[key]) throw new Error(`class-method "${key}" already registered!`);
    else g_classMethods[key]=m;
  },
  /**
   * 注册一种从组件实例获取类别名称的提取器。如果多个提取器都能获得类别名称，用哪个？
   * 这时，需要让组件厂商rendor识别是不是自己的组件，用registerRendorHandler。
   * 组件名称有一定差异，但是肯定会遇到同名冲突：
   * （1）devextreme-vue，组件类别名称是：dxTextBox,dxButton,dxDataGrid
   * （2）v3-easyui，组件类别名称是：LinkButton,Label,DataGrid
   * （3）element-plus，组件类别名称是：ElButton，ElTable
   * （4）ant-design，组件类别名称是：AButton，ATable
   * @param {function} proc 
   */
  registerClassNameGetter(proc){
    g_classNameGetters.push(proc);
  },
  /**
   * 注册一个厂商组件处理器。厂商识别是自家组件，自己处理。
   * @param {function} handler 
   */
  registerRendorHandler(handler){
    g_rendorHandlers.push(handler);
  },
  getMethod(c){
    if (typeof c=='string') return g_classMethods[c];
    else return getClassMethod(c);
  },
  translateCom(t,instWrap,isTranslateChildren=true){
    //console.log(instWrap);
    if (instWrap.$attrs.translate=='no') return;
    /**
     * 先用组件厂商的处理器处理。
     * 不同厂商的组件有其特征，如果能识别就避开了单纯用类别名称识别时名称相同时的冲突。
     */
    for(let i=0;i<g_rendorHandlers.length;i++){
      let handler=g_rendorHandlers[i];
      if (handler(t,instWrap)) return;
    }

    let inst=instWrap.$;
    /**
     * 先检查是否是直接注册的组件类别。
     */
    let {classID,method}=getClassMethod(inst.type);
    let m;
    if (method) m=method;
    else{
      /**
       * 再尝试获取组件类别名称
       */
      let typeName=instWrap.$options.name;
      if (!typeName){
        if (inst.constructor && (inst.constructor.name!='Object')) typeName=inst.constructor.name;
      }
      if (!typeName) typeName=inst.type.name;
      if (!typeName){
        for(let i=0;i<g_classNameGetters.length;i++){
          let getter=g_classNameGetters[i];
          typeName=getter(instWrap);
          if (typeName) break;
        }
      }
      //console.log(typeName,instWrap);
      if (typeName){
        m=g_classMethods[typeName];
      }
    }
    if (m){
      m(t,instWrap);
    }
    if (isTranslateChildren){
      acroMLClassMethod.translateVNode(t,inst.subTree,isTranslateChildren);
    }
  },
  translateVNode(t,vNodes,isTranslateChildren=true){
    function _transVNode(vNode){
      if (!vNode) return; 
      /**
       * vNode.component是vue组件实例。
       * vNode.type是vue组件类，可能没有名称。
       */
      if (vNode.component) acroMLClassMethod.translateCom(t,vNode.component.ctx,false);

      if (isTranslateChildren){
        /**递归翻译子组件 */
        if (vNode.component && vNode.component.subTree) _transVNode(vNode.component.subTree);
        if (vNode.children){
          if (Array.isArray(vNode.children)){
            //console.log('children',vNode,vNode.children.length);
            vNode.children.forEach(function(child){
              _transVNode(child);
            });
          }
          else{
            //console.log('zzzz',vNode.children,vNode);
          }
        }
      }
    }
    if (Array.isArray(vNodes)){
      for(let i=0;i<vNodes.length;i++){
        let vNode=vNodes[i];
        _transVNode(vNode);
      }
    }
    else _transVNode(vNodes);
  },
  translateProp(t,obj,propName,propDefaultDDKey,propDefaultValue){
    if (!obj) return;
    /**
     * 没有属性值，没必要处理?
     * 不行，element-plus的ElTableColumn是不给label就不显示。
     * 但是devextreme-vue的DxColumn的caption可能没设置，但要按data-field来翻译显示。
     * DxDataGrid的groupPanel的emptyPanelText如果没有设置，使用官方的字典翻译。
     */
    //if (!obj[propName]) return;
    if (!obj._acroml_) obj._acroml_={};
    let item=obj._acroml_[propName];
    if (!item){
      item={};
      obj._acroml_[propName]=item;
    }
    let DDKey=item.k;
    let currentValue=obj[propName];
    if (DDKey){
      if (item.v!=currentValue){
        /**
         * 不是我翻译的，人为重新赋值了。
         */
        DDKey=currentValue;
      }
    }
    else{
      DDKey=currentValue;
      if (!DDKey) DDKey=propDefaultDDKey;
      else if (DDKey===propDefaultValue) DDKey=propDefaultDDKey;
    }
    /**
     * 如果DDKey是undefine就让obj[propsName]保持undefined（这样组件会使用其当前字典），
     * 如果赋值''就显示空白了 
     */
    if (DDKey){
      let newValue=t(DDKey);
      if (newValue===DDKey){
        /**
         * 字典中没有翻译结果，如果有预设值，用预设值。
         */
        if (propDefaultValue) newValue=propDefaultValue;
      }
      if (currentValue!=newValue){
        obj[propName]=newValue;
        item.v=newValue;
      }
      if (newValue!=DDKey){
        /**保存原始的DDKey */
        item.k=DDKey;
      }
    }
    else if (propDefaultValue && currentValue!=propDefaultValue) obj[propName]=propDefaultValue;
  },
  translateProps(t,obj,propNames,objDefaultDDKeys,objDefaultValues){
    if (!obj) return;
    if (!propNames){
      propNames=[];
      let copyObj=objDefaultDDKeys||obj;
      for(let propName in copyObj) propNames.push(propName);
    }
    for(let i=0;i<propNames.length;i++){
      let propName=propNames[i];
      let defaultDDKey=objDefaultDDKeys?objDefaultDDKeys[propName]:undefined;
      let defaultValue=objDefaultValues?objDefaultValues[propName]:undefined;
      acroMLClassMethod.translateProp(t,obj,propName,defaultDDKey,defaultValue);
    }
  },
  /**
   * 逐级访问到组件的属性对象，然后翻译属性对象上的属性。
   * @param {function} t 翻译函数
   * @param {object} obj 要翻译的组件
   * @param {array} nodeNames 逐级访问obj属性对象的名称数组，如：['columnChooser','search','editorOptions']，等于是要翻译obj['columnChooser']['search']['editorOptions']。
   * @param {array} propNames 属性对象上要被翻译的属性名称数组。
   * @param {object} objDefaultDDKeys 预设的DDKey对象。
   * @param {object} objDefaultValues 预设的DisplayValue对象，比如组件官方提供的翻译字典。
   * @returns 
   */
  translateNodeProps(t,obj,nodeNames,propNames,objDefaultDDKeys,objDefaultValues){
    if (!obj) return;
    for(let i=0;i<nodeNames.length;i++){
      let nodeName=nodeNames[i];
      obj=obj[nodeName];
      if (!obj) return;
      if (objDefaultDDKeys){
        objDefaultDDKeys=objDefaultDDKeys[nodeName];
      }
      if (objDefaultValues){
        objDefaultValues=objDefaultValues[nodeName];
      }
    }
    acroMLClassMethod.translateProps(t,obj,propNames,objDefaultDDKeys,objDefaultValues);
  },
  translateArray(t,ar,arDefaultDDKeys,arDefaultValues){
    let propNames=[];
    for(let i=0;i<ar.length;i++){
      propNames.push(i);
    }
    acroMLClassMethod.translateProps(t,ar,propNames,arDefaultDDKeys,arDefaultValues);
  },
  t_element(t, instWrap){
    //console.log(instWrap);
    /**
     * 翻译这种组件：<A>File</A>
     * 如果直接翻译innerText，按钮中有图标等模版时，这样翻译影响实现效果 
     */
    function scanNode(node){
      //console.log(node.type,node.nodeName,node.innerText,node.innerHTML);
      /**
       * 只翻译叶子节点的Span，非叶子Span中可能又包了图标等其它元素
       */
      if (node.nodeName=='#text' && node.childNodes.length==0){
        /**
         * 这种模版内的得到的File左右可能有空格，如' File'
         *  <Tag color="#55acee">
              <template #icon>
                <TwitterOutlined />
              </template>
              File
            </Tag>
            用trim去除左右空格
         */
        node.nodeValue=node.nodeValue.trim();
        acroMLClassMethod.translateProp(t,node,'nodeValue');
      }
      for(let i=0;i<node.childNodes.length;i++){
        scanNode(node.childNodes[i]);
      }
    }
    scanNode(instWrap.$el);
  }
}
export default acroMLClassMethod;